﻿using System;
using System.Linq;
using System.Reflection;
using Abp.AspNetCore;
using Abp.Configuration.Startup;
using Abp.Events.Bus;
using Abp.Json;
using Abp.Modules;
using Abp.Timing;
using Magicodes.AppSession;
using Magicodes.AppSession.MvcExtension.Filters;
using Magicodes.WeChat.Application;
using Magicodes.WeChat.Core;
using Magicodes.WeChat.EntityFrameworkCore;
using Magicodes.WeChat.SDK;
using Magicodes.WeChat.SDK.Apis.Token;
using Magicodes.WeChat.User;
using Magicodes.WeChat.User.Dto;
using Magicodes.WeChat.Web.Core.MvcExtension.Filters;
using Microsoft.AspNetCore.Hosting;
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Http.Extensions;
using Microsoft.AspNetCore.Mvc.Filters;

namespace Magicodes.WeChat.Web.Core
{
    [DependsOn(
        typeof(AbpAspNetCoreModule),
        typeof(WeChatCoreModule),
        typeof(WeChatApplicationModule),
        typeof(WeChatDataModule),
        typeof(AppSessionCoreModule)
    )]
    public class WeChatWebCoreModule : AbpModule
    {
        private const string RedirectUrlCookieName = "Magicodes.Wechat_RedirectUrlCookie";
        private const string State = "magicodes.wechat";

        public override void PreInitialize()
        {
        }

        public override void Initialize()
        {
            IocManager.RegisterAssemblyByConvention(Assembly.GetExecutingAssembly());
        }

        public override void PostInitialize()
        {
            //设置微信页面绑定路径
            AppBaseController.LoginUrls.Add(RequestTypes.WeChat, "/jxywx/Account/WeChatBind");

            #region 注册用户信息获取逻辑
            var manager = IocManager.Resolve<IAppSessionManager>();
            manager?.RegisterFuncForGetUserExtendInfo(RequestTypes.WeChat,
                appSession =>
                {
                    var service = IocManager.Resolve<IUserService>();
                    return service.GetUserInfo().Result;
                }
            );
            #endregion

            #region 注册粉丝信息获取逻辑
            manager?.RegisterFuncForGetUserInfo(RequestTypes.WeChat,
                appSession => IocManager.Resolve<IWeChatUserAppService>().GetWeChatUserById(appSession.AppUserId)
                    .Result);
            #endregion

            #region 注册微信授权请求处理逻辑
            AppBaseController.RequestTypeActions.Add(RequestTypes.WeChat, (context, controller) =>
                    {
                        if (!context.Filters.Any(item => item is WeChatOAuthAttribute))
                        {
                            Logger.WarnFormat("Action:{0}没有设置WeChatOAuthAttribute，将不会进行微信验证。",
                                context.ActionDescriptor.DisplayName);
                            return;
                        }
                        WeChatUserListDto weChatUser = null;
                        var weChatUserAppService = IocManager.Resolve<IWeChatUserAppService>();
                        //var multiTenancyConfig = IocManager.Resolve<IMultiTenancyConfig>();
                        var hostingEnvironment = IocManager.Resolve<IHostingEnvironment>();
                        var appSession = controller.AppSession;
                        var appTenantManager = IocManager.Resolve<IAppTenantManager>();
                        var isDevelopment = false;
                        Logger.InfoFormat("Action:{0}正在获取授权并且获得粉丝信息。", context.ActionDescriptor.DisplayName);
                        if (hostingEnvironment != null && hostingEnvironment.IsDevelopment())
                        {
                            Logger.Debug("正在使用开发环境...");
                            isDevelopment = true;
                        }
                        var httpContext = context.HttpContext;
                        var request = httpContext.Request;
                        if (weChatUserAppService == null)
                            throw new ArgumentNullException(nameof(weChatUserAppService));
                        {
                            Logger.Debug("正在从缓存中获取粉丝信息...");
                            if (!string.IsNullOrEmpty(appSession.AppSessionInfo?.AppUserId))
                                weChatUser = appSession.GetUserInfo<WeChatUserListDto>();
                            //如果用户已经验证或者已经获取微信信息，则不再验证（提高效率）
                            if (weChatUser != null)
                            {
                                Logger.Debug("已从数据库或缓存中获取粉丝信息：" + weChatUser.Id);
                                context.HttpContext.Items["WeChatUser"] = weChatUser;
                                if (GetWeChatBindUserInfo(context)) return;
                                return;
                            }
                            Logger.Debug("缓存信息不存在...");
                            var tenantId = appTenantManager.GetTenantId();
                            if (isDevelopment)
                            {
                                //开发模式使用测试数据
                                weChatUser = new WeChatUserListDto
                                {
                                    City = "长沙",
                                    Country = "中国",
                                    Id = Guid.NewGuid().ToString("N"),
                                    GroupId = 0,
                                    NickName = "雪雁",
                                    SubscribeTime = DateTime.Now,
                                    Subscribe = true,
                                    Province = "湖南",
                                    HeadImgUrl = "/PlugIns/Jxy.WeChat.Web.Mvc/wwwroot/images/user.png"
                                };
                                appSession.SetAppSession(new AppSessionInfo
                                {
                                    AppUserId = weChatUser.Id,
                                    AppType = WeChatCoreConsts.Name,
                                    RequestType = RequestTypes.WeChat
                                });
                                context.HttpContext.Items["WeChatUser"] = weChatUser;
                                if (GetWeChatBindUserInfo(context)) return;
                                return;
                            }

                            var code = request.Query["code"];
                            var state = request.Query["state"];

                            #region 如果是从微信验证页面跳转回来

                            if (!string.IsNullOrEmpty(code) && !string.IsNullOrEmpty(state))
                            {
                                Logger.DebugFormat("根据授权Code获取粉丝信息。Code:{0}   State:{1} ...", code, state);
                                //TODO:判断是否来自open.weixin.qq.com/connect/oauth2/authorize
                                //if (request.)
                                //{

                                //}
                                //通过code换取access_token,Code只能用一次
                                //网页授权接口调用凭证,注意：此access_token与基础支持的access_token不同
                                var result = WeChatApisContext.Current.OAuthApi.Get(code);
                                if (!result.IsSuccess())
                                {
                                    if (result.ReturnCode == SDK.Apis.ReturnCodes.OAUTH授权码已被使用)
                                    {
                                        Logger.Warn(result.ReturnCode + "!正在重新跳转以进行授权！");
                                        CreateRedirectUrl(context, httpContext);
                                        return;
                                    }
                                    throw new Exception("授权出错，获取access_token失败！");
                                }
                                Logger.Debug("授权成功：" + result.DetailResult);

                                //从数据库或缓存中获取
                                weChatUser = weChatUserAppService.GetWeChatUserById(result.OpenId).Result;
                                ;

                                if (weChatUser == null)
                                {
                                    #region 获取微信用户信息

                                    var oAuthApi = new OAuthApi();
                                    oAuthApi.SetKey(tenantId);
                                    var userInfoResult = oAuthApi.GetUserInfo(result.AccessToken, result.OpenId);

                                    if (!userInfoResult.IsSuccess())
                                    {
                                        Logger.Warn("通过OAUTH接口获取粉丝信息失败：" + userInfoResult.DetailResult);
                                        throw new Exception("授权出错，获取粉丝信息失败！");
                                    }
                                    try
                                    {
                                        Logger.Debug("通过OAUTH接口获取粉丝信息：" + userInfoResult.DetailResult);
                                        var weChatUserForEdit = new WeChatUserEditDto
                                        {
                                            City = userInfoResult.City,
                                            Country = userInfoResult.Country,
                                            //GroupId = userInfo.groupid,
                                            HeadImgUrl = userInfoResult.Headimgurl,
                                            //Language = userInfo.language,
                                            NickName = userInfoResult.NickName,
                                            Id = userInfoResult.OpenId,
                                            Province = userInfoResult.Province,
                                            //Remark = userInfo.remark,
                                            Sex = (int)userInfoResult.Sex,
                                            Subscribe = false,
                                            SubscribeTime = DateTime.Now,
                                            UnionId = userInfoResult.Unionid,
                                            TenantId = tenantId,
                                            AppId = oAuthApi.AppConfig.AppId
                                        };
                                        //if (multiTenancyConfig.IsEnabled)
                                        //    weChatUserForEdit.TenantId = tenantId;
                                        Logger.Debug("正在准备创建或更新粉丝信息：" + userInfoResult.OpenId);
                                        weChatUserAppService.CreateOrUpdateWeChatUser(
                                                                            new CreateOrUpdateWeChatUserDto { WeChatUser = weChatUserForEdit });
                                        //Logger.Debug("准备从缓存中获取粉丝信息：" + userInfoResult.OpenId);
                                        //weChatUser = weChatUserAppService.GetWeChatUserById(weChatUserForEdit.Id).Result;
                                        //Logger.Debug("已经从缓存中获取粉丝信息：" + (weChatUser == null ? "weChatUser is NULL" : weChatUser.Id));
                                        weChatUser = new WeChatUserListDto()
                                        {
                                            City = userInfoResult.City,
                                            Country = userInfoResult.Country,
                                            //GroupId = userInfo.groupid,
                                            HeadImgUrl = userInfoResult.Headimgurl,
                                            //Language = userInfo.language,
                                            NickName = userInfoResult.NickName,
                                            Id = userInfoResult.OpenId,
                                            Province = userInfoResult.Province,
                                            //Remark = userInfo.remark,
                                            Sex = (int)userInfoResult.Sex,
                                            Subscribe = false,
                                            SubscribeTime = DateTime.Now,
                                            UnionId = userInfoResult.Unionid,
                                            CreationTime = Clock.Now,
                                        };

                                        context.HttpContext.Items["WeChatUser"] = weChatUser;

                                    }
                                    catch (Exception ex)
                                    {
                                        Logger.Error(string.Format("{0}\n{1}\n{2}", request.Path, ex.Message, ex));
                                        //filterContext.Result = new ContentResult { Content = "授权出错：" + code + "     具体错误：" + ex.Message };
                                        throw new Exception("授权出错：" + code + "     具体错误：" + ex.Message);
                                    }

                                    #endregion
                                }
                                if (appSession == null)
                                {
                                    Logger.Warn("appSession为null，准备从容器中获取...");
                                    appSession = IocManager.Resolve<IAppSession>();
                                }
                                Logger.Warn("准备设置AppSession...");
                                appSession.SetAppSession(new AppSessionInfo
                                {
                                    AppType = WeChatCoreConsts.Name,
                                    AppUserId = weChatUser.Id,
                                    RequestType = RequestTypes.WeChat
                                });
                                Logger.Debug("已设置SetAppSession");
                                Logger.Debug("判断是否需要绑定会员...");
                                if (GetWeChatBindUserInfo(context)) return;
                                var redirectUrlCookie = request.Cookies[RedirectUrlCookieName];
                                if (string.IsNullOrEmpty(redirectUrlCookie)) return;
                                var redirectUrl = redirectUrlCookie;
                                httpContext.Response.Cookies.Delete(RedirectUrlCookieName);
                                context.Result = new RedirectResult(redirectUrl);
                            }

                            #endregion

                            #region 如果没有验证，则进行验证

                            else
                            {
                                CreateRedirectUrl(context, httpContext);
                            }

                            #endregion
                        }
                    });
            #endregion


        }

        private void CreateRedirectUrl(ActionExecutingContext context, Microsoft.AspNetCore.Http.HttpContext httpContext)
        {
            Logger.DebugFormat("即将跳转到微信服务器来并授权获取粉丝信息");
            var redirectUrl = context.HttpContext.Request.GetDisplayUrl();
            httpContext.Response.Cookies.Append(RedirectUrlCookieName, redirectUrl);
            //获取授权Url
            var url = WeChatApisContext.Current.OAuthApi.GetAuthorizeUrl(redirectUrl, State);
            Logger.Debug(string.Format("跳转至微信服务器获取授权...\n{0}\n{1}", redirectUrl, url));
            context.Result = new RedirectResult(url);
        }

        /// <summary>
        /// 验证并获取会员信息
        /// </summary>
        /// <param name="context"></param>
        /// <returns></returns>
        private bool GetWeChatBindUserInfo(ActionExecutingContext context)
        {
            if (!context.Filters.Any(item => item is UserMustOAuthAttribute))
                return false;
            var url = context.HttpContext.Request.GetDisplayUrl();
            Logger.DebugFormat("UserMustOAuth:{0}", url);
            if (context.Filters.Any(item => item is UserIgnoreOAuthAttribute))
            {
                Logger.DebugFormat("UserIgnoreOAuth:{0}", url);
                return false;
            }

            var userService = IocManager.Resolve<IUserService>();
            var user = userService.GetUserInfo().Result;
            if (user == null)
            {
                var redirectUrl = AppBaseController.LoginUrls[RequestTypes.WeChat];
                Logger.DebugFormat("user is Null,redirectUrl:{0}", redirectUrl);
                context.Result = new RedirectResult(redirectUrl);
                return true;
            }
            Logger.InfoFormat("User:{0}", user.ToJsonString());
            context.HttpContext.Items["User"] = user;
            return false;
        }


    }
}